/* Copyright Thanh-Trung Pham, JAIST, 2013 */

package jp.ac.jaist.computation;

import java.util.*;

import jp.ac.jaist.*;
import jp.ac.jaist.BranchingStructureType.IfPart;
import jp.ac.jaist.MultiTryCatchStructureType.MultiTryCatchPart;
import jp.ac.jaist.input.*;
import jp.ac.jaist.model.RMPMarkovModel;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.keyvalue.MultiKey;
import org.apache.commons.collections.map.MultiKeyMap;

/**
 *
 * @author Thanh-Trung Pham
 */
public class RMPReliabilityCalculator {

    private static final String RMP_DOT = ".";
    private static RMPReliabilityCalculator m_oCalculator;
    private UsageProfilePartType m_oUsageProfilePart;
    private Map<String, RMPInternalActivityWrapper> m_oTransformedProvidedServices;

    /**
     * The constructor.
     */
    private RMPReliabilityCalculator() {
        m_oTransformedProvidedServices = new HashMap<String, RMPInternalActivityWrapper>();
    }

    /**
     * Get all the transformed provided services.
     * @return all the transformed provided services.
     */
    public Map<String, RMPInternalActivityWrapper> getTransformedProvidedServices() {
        return m_oTransformedProvidedServices;
    }

    /**
     * Get the only instance.
     * @return the only instance.
     */
    public static synchronized RMPReliabilityCalculator getInstance() {
        if (m_oCalculator == null) {
            m_oCalculator = new RMPReliabilityCalculator();
        }

        return m_oCalculator;
    }

    /**
     * Clear all the cached data.
     */
    private void clearCachedData() {
        m_oUsageProfilePart = null;
        m_oTransformedProvidedServices.clear();
    }

    /**
     * Calculate the reliability-related probabilities for the input model.
     * @return an equivalent internal activity with the reliability-related probabilities.
     */
    public synchronized RMPInternalActivityWrapper calculateReliabilityRelatedProbabilities() {
        RMPInputModelManager oInputModelManager = RMPInputModelManager.getInstance();
        UsageProfileType oUsageProfile = oInputModelManager.getUsageProfile();


        RMPInternalActivityWrapper oReturnIAWrapper = new RMPInternalActivityWrapper();
        UsageProfilePartType[] oUsageProfileParts = oUsageProfile.getUsageProfilePartArray();

        for (UsageProfilePartType oUsageProfilePart : oUsageProfileParts) {
            clearCachedData();

            m_oUsageProfilePart = oUsageProfilePart;
            double dUsageProfilePartProbability = oUsageProfilePart.getProbability();

            UserInterfaceType oUserInterface = oInputModelManager.getUserInterfaceByName(m_oUsageProfilePart.getForUserInterface());
            String strComponentInstanceName = oUserInterface.getFromService().getFromComponentInstance();
            String strProvidedServiceName = oUserInterface.getFromService().getFromProvidedService();

            RMPInternalActivityWrapper oEquivalentIAWrapper = transformIntoEquivalentIA(strComponentInstanceName, strProvidedServiceName);

            for(int i=0; i< oInputModelManager.getNumberOfFailureTypes(); i++){
                oReturnIAWrapper.setFp(i, oReturnIAWrapper.getFp(i)+ oEquivalentIAWrapper.getFp(i) * dUsageProfilePartProbability);
            }
        }

        return oReturnIAWrapper;
    }

    /**
     * Transform a provided service implementation into an equivalent internal activity.
     *
     * @param componentInstanceName the current component instance name.
     * @param providedServiceName   a provide service name.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, String providedServiceName) {
        String strFullProvidedServiceName = getFullProvidedServiceName(componentInstanceName, providedServiceName);
        if (m_oTransformedProvidedServices.containsKey(strFullProvidedServiceName)) {
            return m_oTransformedProvidedServices.get(strFullProvidedServiceName);
        }

        RMPInputModelManager oInputModelManager = RMPInputModelManager.getInstance();

        ComponentInstanceType oComponentInstance = oInputModelManager.getComponentInstance(oInputModelManager.getSystemArchitecture(),componentInstanceName);
        ServiceImplementationType oServiceImplementation = oInputModelManager.getServiceImplementation(oInputModelManager.getComponentByName(oComponentInstance.getForComponent()), providedServiceName);

        RMPInternalActivityWrapper oInternalActivity = transformIntoEquivalentIA(componentInstanceName, oServiceImplementation);

        m_oTransformedProvidedServices.put(strFullProvidedServiceName, oInternalActivity);
        return oInternalActivity;
    }

    /**
     * Transform the call propagation into an equivalent internal activity.
     * @param componentInstanceName the current component instance.
     * @param callPropagation the call propagation.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, CallPropagationType callPropagation) {
        RMPInternalActivityWrapper oIAWrapper = null;

        if (callPropagation.isSetInternalActivity()) {
            oIAWrapper = new RMPInternalActivityWrapper(callPropagation.getInternalActivity());
        } else if (callPropagation.isSetCallingActivity()) {
            oIAWrapper = transformIntoEquivalentIA(componentInstanceName, callPropagation.getCallingActivity());
        } else if (callPropagation.isSetBranchingStructure()) {
            oIAWrapper = transformIntoEquivalentIA(componentInstanceName, callPropagation.getBranchingStructure());
        } else if (callPropagation.isSetLoopingStructure()) {
            oIAWrapper = transformIntoEquivalentIA(componentInstanceName, callPropagation.getLoopingStructure());
        } else if (callPropagation.isSetSequentialStructure()) {
            oIAWrapper = transformIntoEquivalentIA(componentInstanceName, callPropagation.getSequentialStructure());
        } else if (callPropagation.isSetParallelStructure()) {
            oIAWrapper = transformIntoEquivalentIA(componentInstanceName, callPropagation.getParallelStructure());
        } else if (callPropagation.isSetRetryStructure()) {
            oIAWrapper = transformIntoEquivalentIA(componentInstanceName, callPropagation.getRetryStructure());
        } else if (callPropagation.isSetMultiTryCatchStructure()) {
            oIAWrapper = transformIntoEquivalentIA(componentInstanceName, callPropagation.getMultiTryCatchStructure());
        } else if(callPropagation.isSetMVPStructure()){
            oIAWrapper = transformIntoEquivalentIA(componentInstanceName, callPropagation.getMVPStructure());
        }

        return oIAWrapper;
    }

    /**
     * Transform a MVP Structure into an equivalent internal activity.
     * @param componentInstanceName the current component instance name.
     * @param mvpStructure the MVP structure.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, MVPStructureType mvpStructure) {
        int iMaxTI = mvpStructure.getMaxToleratedIncompletion();
        int minMS = mvpStructure.getMinMajoritySize();

        String strDetectedFailureType = mvpStructure.getDetectedFailureType();
        String strUndetectedFailureType = mvpStructure.getUndetectedFailureType();

        MVPStructureType.MVPPart[] oMVPParts = mvpStructure.getMVPPartArray();
        int iNumberOfMVPParts = oMVPParts.length;
        RMPAgreementOfErrorsVectorWrapper oAgreementOfErrorsVectorWrapper = new RMPAgreementOfErrorsVectorWrapper(mvpStructure.getAgreementOfErrorsVector(), minMS, iNumberOfMVPParts);

        MVPStructureType.ErrorPropertyVector[] oErrorPropertyVectors = mvpStructure.getErrorPropertyVectorArray();
        RMPErrorPropertyVectorWrapper[] oErrorPropertyVectorWrappers = new RMPErrorPropertyVectorWrapper[iNumberOfMVPParts];


        //calculate all the probabilities of possibilities.
        MultiKeyMap oProbabilitiesOfPossibilities = new MultiKeyMap();
        oProbabilitiesOfPossibilities.put(0,0,0,1.0);
        for(int i=0; i<iNumberOfMVPParts; i++){
            oErrorPropertyVectorWrappers[i] = new RMPErrorPropertyVectorWrapper(oErrorPropertyVectors[i]);
            oProbabilitiesOfPossibilities = calculateProbabilitiesOfPossibilities(oProbabilitiesOfPossibilities, transformIntoEquivalentIA(componentInstanceName, oMVPParts[i]),oErrorPropertyVectorWrappers[i]);
        }

        //voter
        double dProbOfCorOut = 0.0;
        double dProbOfErrOut = 0.0;
        Iterator oIterator = oProbabilitiesOfPossibilities.keySet().iterator();
        while(oIterator.hasNext()){
            MultiKey oPossibility = (MultiKey) oIterator.next();

            int iX = ((Integer)oPossibility.getKey(0)).intValue();
            int iY = ((Integer)oPossibility.getKey(1)).intValue();
            int iZ = ((Integer)oPossibility.getKey(2)).intValue();

            if(iZ<=iMaxTI && iX>=minMS){
                dProbOfCorOut +=  ((Double) oProbabilitiesOfPossibilities.get(iX, iY, iZ)).doubleValue();
            }else if (iZ<=iMaxTI && iX<minMS && iY >= minMS ){
                dProbOfErrOut +=  ((Double) oProbabilitiesOfPossibilities.get(iX, iY, iZ)).doubleValue() * oAgreementOfErrorsVectorWrapper.getAgreementOfErrors(iY);
            }
        }

        RMPInternalActivityWrapper oReturnIAWrapper = new RMPInternalActivityWrapper();
        oReturnIAWrapper.setSp(dProbOfCorOut);
        oReturnIAWrapper.setFp(RMPInputModelManager.getInstance().getFailureTypeIndex(strUndetectedFailureType),dProbOfErrOut);
        oReturnIAWrapper.setFp(RMPInputModelManager.getInstance().getFailureTypeIndex(strDetectedFailureType), 1-dProbOfCorOut-dProbOfErrOut);

        return oReturnIAWrapper;
    }



    /**
     * Calculate all the probabilities of possibilities after the execution of an MVPPart.
     * @param probabilitiesOfPossibilities all the probabilities of possibilities before.
     * @param iaWrapper the equivalent IA of the MVPPart.
     * @param errorPropertyVectorWrapper the error property vector wrapper
     * @return  all the probabilities of possibilities after the execution of an MVPPart.
     */
    private MultiKeyMap calculateProbabilitiesOfPossibilities(MultiKeyMap probabilitiesOfPossibilities, RMPInternalActivityWrapper iaWrapper, RMPErrorPropertyVectorWrapper errorPropertyVectorWrapper){
        MultiKeyMap oReturnMultiKeyMap = new MultiKeyMap();
        RMPInputModelManager oRMPRmpInputModelManager = RMPInputModelManager.getInstance();
        int iNumberOfFailureTypes = oRMPRmpInputModelManager.getNumberOfFailureTypes();

        double dProbabilityOfIncreasingX = iaWrapper.getSp();

        double dProbabilityOfIncreasingY = 0.0;
        double dProbabilityOfIncreasingZ = 0.0;

        for(int j=1; j< iNumberOfFailureTypes; j++){
            dProbabilityOfIncreasingY+=iaWrapper.getFp(j)*(1-errorPropertyVectorWrapper.getProbability(j));
            dProbabilityOfIncreasingZ+=iaWrapper.getFp(j)*errorPropertyVectorWrapper.getProbability(j);
        }

        Iterator oIterator = probabilitiesOfPossibilities.keySet().iterator();
        while(oIterator.hasNext()){
            MultiKey oPossibility = (MultiKey) oIterator.next();

            int iX = ((Integer)oPossibility.getKey(0)).intValue();
            int iY = ((Integer)oPossibility.getKey(1)).intValue();
            int iZ = ((Integer)oPossibility.getKey(2)).intValue();

            double dProbability = ((Double) probabilitiesOfPossibilities.get(iX, iY, iZ)).doubleValue();
            if(oReturnMultiKeyMap.containsKey(iX+1,iY,iZ)){
                oReturnMultiKeyMap.put(iX+1,iY,iZ,((Double)oReturnMultiKeyMap.get(iX+1,iY,iZ)).doubleValue()+ dProbability *dProbabilityOfIncreasingX);
            }else{
                oReturnMultiKeyMap.put(iX+1,iY,iZ, dProbability *dProbabilityOfIncreasingX);
            }

            if(oReturnMultiKeyMap.containsKey(iX,iY+1,iZ)){
                oReturnMultiKeyMap.put(iX,iY+1,iZ,((Double)oReturnMultiKeyMap.get(iX,iY+1,iZ)).doubleValue()+ dProbability *dProbabilityOfIncreasingY);
            }else{
                oReturnMultiKeyMap.put(iX,iY+1,iZ, dProbability *dProbabilityOfIncreasingY);
            }

            if(oReturnMultiKeyMap.containsKey(iX,iY,iZ+1)){
                oReturnMultiKeyMap.put(iX,iY,iZ+1,((Double)oReturnMultiKeyMap.get(iX,iY,iZ+1)).doubleValue()+ dProbability *dProbabilityOfIncreasingZ);
            }else{
                oReturnMultiKeyMap.put(iX,iY,iZ+1, dProbability *dProbabilityOfIncreasingZ);
            }
        }

        return oReturnMultiKeyMap;
    }



    /**
     * Transform a calling activity into an equivalent internal activity.
     * @param componentInstanceName the current component instance name.
     * @param callingActivity a calling activity.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, CallingActivityType callingActivity) {
        String strCalledService = callingActivity.getCalledService();

        RMPInputModelManager oInputModelManager = RMPInputModelManager.getInstance();

        ComponentConnectorType oComponentConnector = oInputModelManager.getComponentConnectorFor(oInputModelManager.getSystemArchitecture(), componentInstanceName, strCalledService);
        String strFromComponentInstance = oComponentConnector.getFromService().getFromComponentInstance();
        String strFromProvidedServiceName = oComponentConnector.getFromService().getFromProvidedService();

        return transformIntoEquivalentIA(strFromComponentInstance, strFromProvidedServiceName);
    }

    /**
     * Transform a branching structure into an equivalent internal activity.
     * @param componentInstanceName the current component instance name.
     * @param branchingStructure a branching structure.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, BranchingStructureType branchingStructure) {
        RMPInputModelManager oInputModelManager = RMPInputModelManager.getInstance();

        double dProbabilityOfAllIfBranches = 0.0;
        IfPart[] oIfParts = branchingStructure.getIfPartArray();

        List<RMPInternalActivityWrapper> oIAWrappers = new ArrayList<RMPInternalActivityWrapper>();
        List<Double> oBranchingProbabilities = new ArrayList<Double>();

        for (IfPart oIfPart : oIfParts) {
            oIAWrappers.add(transformIntoEquivalentIA(componentInstanceName, oIfPart));

            String strBranchingCondition = oIfPart.getBranchingCondition();
            Double oBranchingProbability = oInputModelManager.getUsageProfilePartBCProbability(m_oUsageProfilePart, strBranchingCondition).getValue();
            oBranchingProbabilities.add(oBranchingProbability);

            dProbabilityOfAllIfBranches += oBranchingProbability.doubleValue();
        }

        if(branchingStructure.getElsePart() == null){
            RMPInternalActivityWrapper oElsePartIAWrapper = new RMPInternalActivityWrapper();
            oElsePartIAWrapper.setSp(1.0);
            oIAWrappers.add(oElsePartIAWrapper);
        }else{
            oIAWrappers.add(transformIntoEquivalentIA(componentInstanceName, branchingStructure.getElsePart()));
        }

        oBranchingProbabilities.add(1.0 - dProbabilityOfAllIfBranches);

        RMPInternalActivityWrapper oIAWrapper = new RMPInternalActivityWrapper();
        double dSp = 0.0;
        for (int i = 0; i < oIAWrappers.size(); i++) {
            dSp += (oBranchingProbabilities.get(i) * oIAWrappers.get(i).getSp());
        }
        oIAWrapper.setSp(dSp);

        for (int j = 1; j < oInputModelManager.getNumberOfFailureTypes(); j++) {
            double fpj = 0.0;
            for (int i = 0; i < oIAWrappers.size(); i++) {
                fpj += (oBranchingProbabilities.get(i) * oIAWrappers.get(i).getFp(j));
            }

            oIAWrapper.setFp(j, fpj);
        }

        return oIAWrapper;

    }

    /**
     * Transform a looping structure into an equivalent internal activity.
     * @param componentInstanceName the current component instance name.
     * @param loopingStructure a looping structure.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, LoopingStructureType loopingStructure) {
        RMPInputModelManager oInputModelManager = RMPInputModelManager.getInstance();
        RMPInternalActivityWrapper oReturnIAWrapper = new RMPInternalActivityWrapper();
        RMPInternalActivityWrapper oIAWrapper = transformIntoEquivalentIA(componentInstanceName, loopingStructure.getLoopingPart());

        UsageProfilePartType.LCAverage oLCAverage = oInputModelManager.getUsageProfilePartLCAverage(m_oUsageProfilePart, loopingStructure.getLoopCount());
        if(oLCAverage != null){
            int iLoopCountAverage = oLCAverage.getValue();
            oReturnIAWrapper.setSp(Math.pow(oIAWrapper.getSp(), iLoopCountAverage));

            for (int j = 1; j < oInputModelManager.getNumberOfFailureTypes(); j++) {
                double fpj = 0.0;
                double dPartialSp = 1.0;
                for (int i = 1; i <= iLoopCountAverage; i++) {
                    fpj += dPartialSp * oIAWrapper.getFp(j);
                    dPartialSp*=oIAWrapper.getSp();
                }

                oReturnIAWrapper.setFp(j, fpj);
            }
        }
        else{
            UsageProfilePartType.LCProbabilityDistribution oLCProbabilityDistribution = oInputModelManager.getUsageProfilePartLCProbabilityDistribution(m_oUsageProfilePart, loopingStructure.getLoopCount());
            RMPLCProbabilityDistributionWrapper oLCDistributionWrapper = new RMPLCProbabilityDistributionWrapper(oLCProbabilityDistribution);
            Integer[] oLCValues = oLCDistributionWrapper.getLCValues();

            double dSP =0.0;
            for(int i=0; i< oLCValues.length; i++){
                dSP+=oLCDistributionWrapper.getProbability(oLCValues[i])*Math.pow(oIAWrapper.getSp(), oLCValues[i]);
            }

            oReturnIAWrapper.setSp(dSP);

            for (int j = 1; j < oInputModelManager.getNumberOfFailureTypes(); j++) {
                double fpj = 0.0;

                for(int i=0; i< oLCValues.length; i++){
                    double dInnerSum = 0.0;
                    double dPartialSp = 1.0;
                    for(int k=1; k<=oLCValues[i]; k++){
                        dInnerSum+=dPartialSp*oIAWrapper.getFp(j);
                        dPartialSp*=oIAWrapper.getSp();
                    }

                    fpj+=oLCDistributionWrapper.getProbability(oLCValues[i])* dInnerSum;
                }

                oReturnIAWrapper.setFp(j,fpj);
            }
        }

        return oReturnIAWrapper;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#clone()
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /**
     * Transform a sequential structure into an equivalent internal activity.
     * @param componentInstanceName the current component instance name.
     * @param sequentialStructure a sequential structure.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, SequentialStructureType sequentialStructure) {
        List<RMPInternalActivityWrapper> oIAWrappers = new ArrayList<RMPInternalActivityWrapper>();

        for (int i = 0; i < sequentialStructure.getSequentialPartArray().length; i++) {
            oIAWrappers.add(transformIntoEquivalentIA(componentInstanceName, sequentialStructure.getSequentialPartArray()[i]));
        }

        RMPInternalActivityWrapper oIAWrapper = new RMPInternalActivityWrapper();
        double dSp = 1.0;
        for (int i = 0; i < oIAWrappers.size(); i++) {
            dSp *= oIAWrappers.get(i).getSp();
        }
        oIAWrapper.setSp(dSp);

        int iNumberOfFailureTypes = RMPInputModelManager.getInstance().getNumberOfFailureTypes();
        for (int j = 1; j < iNumberOfFailureTypes; j++) {
            double fpj = 0.0;
            double dPartialSp = 1.0;
            for (int i = 0; i < oIAWrappers.size(); i++) {
                fpj += dPartialSp * oIAWrappers.get(i).getFp(j);
                dPartialSp*=oIAWrappers.get(i).getSp();
            }

            oIAWrapper.setFp(j, fpj);
        }

        return oIAWrapper;
    }

    /**
     * Transform a parallel structure into an equivalent internal activity.
     * @param componentInstanceName the current component instance name.
     * @param parallelStructure a parallel structure.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, ParallelStructureType parallelStructure) {
        CallPropagationType[] oParallelParts = parallelStructure.getParallelPartArray();

        RMPInternalActivityWrapper[] oIAWrappers = new RMPInternalActivityWrapper[oParallelParts.length];

        for (int i = 0; i < oParallelParts.length; i++) {
            oIAWrappers[i] = transformIntoEquivalentIA(componentInstanceName, oParallelParts[i]);
        }

        RMPInternalActivityWrapper oIAWrapper = new RMPInternalActivityWrapper();

        double dSp = 1.0;
        for (int i = 0; i < oIAWrappers.length; i++) {
            dSp *= oIAWrappers[i].getSp();
        }
        oIAWrapper.setSp(dSp);

        int numberOfFailureTypes = RMPInputModelManager.getInstance().getNumberOfFailureTypes();
        for (int j = 1; j < numberOfFailureTypes; j++) {
            double fpj = 0.0;
            for (int i = 0; i < oIAWrappers.length; i++) {
                double dPrefix = 1.0;
                for (int k = 0; k <= i - 1; k++) {
                    double dInnerPrefix = 0.0;
                    for (int l = j; l < numberOfFailureTypes; l++) {
                        dInnerPrefix += oIAWrappers[k].getFp(l);
                    }

                    dPrefix *= (1.0 - dInnerPrefix);
                }

                double dSuffix = 1.0;
                for (int k = i + 1; k < oIAWrappers.length; k++) {
                    double dInnerSuffix = 0.0;
                    for (int l = j + 1; l < numberOfFailureTypes; l++) {
                        dInnerSuffix += oIAWrappers[k].getFp(l);
                    }

                    dSuffix *= (1.0 - dInnerSuffix);
                }

                fpj += dPrefix * oIAWrappers[i].getFp(j) * dSuffix;
            }

            oIAWrapper.setFp(j, fpj);
        }

        return oIAWrapper;
    }

    /**
     * Transform a retry structure into an equivalent internal activity.
     * @param componentInstanceName the current component instance name.
     * @param retryStructure a retry structure.
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, RetryStructureType retryStructure) {
        RMPInternalActivityWrapper oEquivalentIAWrapper = transformIntoEquivalentIA(componentInstanceName, retryStructure.getRetryPart());

        RMPMarkovModel oMarkovModel = new RMPMarkovModel();
        //START state
        oMarkovModel.createSTARTState("START");

        RMPInputModelManager oInputModelManager = RMPInputModelManager.getInstance();
        int iNumberOfFailureTypes = oInputModelManager.getNumberOfFailureTypes();

        //F0(SUCCESS), F1, F2 ...., Fm
        for (int j = 0; j < iNumberOfFailureTypes; j++) {
            oMarkovModel.addAbsorbingMarkovState("F" + j);
        }

        //build a Markov model for each retry part.
        int iRetryCount = retryStructure.getRetryCount();
        for (int i = 0; i <= iRetryCount; i++) {
            String strSTARTi = "START" + i;
            String strRPretryi = "RPretry" + i;

            oMarkovModel.addTransitiveMarkovState(strSTARTi);
            oMarkovModel.addTransitiveMarkovState(strRPretryi);

            oMarkovModel.addMarkovTransition(strSTARTi, strRPretryi, 1.0);

            for (int j = 0; j < iNumberOfFailureTypes; j++) {
                String strFij = "F" + i + "," + j;
                oMarkovModel.addTransitiveMarkovState(strFij);
                oMarkovModel.addMarkovTransition(strRPretryi, strFij, oEquivalentIAWrapper.getFp(j));
            }
        }

        //transition from START->START0
        oMarkovModel.addMarkovTransition("START", "START0", 1.0);

        //for the Markov model of the last retry
        for (int j = 0; j < iNumberOfFailureTypes; j++) {
            oMarkovModel.addMarkovTransition("F" + iRetryCount + "," + j, "F" + j, 1.0);
        }

        HandledFailureTypeType[] oHandledFailureTypes = retryStructure.getHandledFailureTypeArray();
        ErrorDetectionMatrixType oErrorDetectionMatrix = retryStructure.getErrorDetectionMatrix();
        RMPErrorDetectionMatrixWrapper oEDMatrixWrapper = new RMPErrorDetectionMatrixWrapper(oErrorDetectionMatrix);

        //for other Markov models
        for (int i = 0; i < iRetryCount; i++) {
            for (int j = 0; j < iNumberOfFailureTypes; j++) {
                double dTotalDetectionProbability = 0.0;
                for (HandledFailureTypeType oHandledFailureType : oHandledFailureTypes) {
                    int k = oInputModelManager.getFailureTypeIndex(oHandledFailureType.getForFailureType());
                    dTotalDetectionProbability += oEDMatrixWrapper.getDp(j, k);
                }

                String strFij = "F" + i + "," + j;
                oMarkovModel.addMarkovTransition(strFij, "START" + (i + 1), dTotalDetectionProbability);
                oMarkovModel.addMarkovTransition(strFij, "F" + j, 1.0 - dTotalDetectionProbability);
            }
        }

        RMPInternalActivityWrapper oIAWrapper = new RMPInternalActivityWrapper();
        for (int j = 0; j < iNumberOfFailureTypes; j++) {
            oIAWrapper.setFp(j, oMarkovModel.getProbability("F" + j));
        }

        return oIAWrapper;
    }

    /**
     * Transform a multi try catch structure into an equivalent internal activity.
     * @param componentInstanceName the current component instance name.
     * @param multiTryCatchStructure a multi try catch structure
     * @return the equivalent internal activity.
     */
    private RMPInternalActivityWrapper transformIntoEquivalentIA(String componentInstanceName, MultiTryCatchStructureType multiTryCatchStructure) {
        MultiTryCatchPart[] oMultiTryCatchParts = multiTryCatchStructure.getMultiTryCatchPartArray();
        int iNumberOfInnerParts = oMultiTryCatchParts.length;

        RMPInternalActivityWrapper[] oEquivalentIAWrappers = new RMPInternalActivityWrapper[iNumberOfInnerParts];

        for (int i = 0; i < iNumberOfInnerParts; i++) {
            oEquivalentIAWrappers[i] = transformIntoEquivalentIA(componentInstanceName, oMultiTryCatchParts[i]);
        }

        RMPMarkovModel oMarkovModel = new RMPMarkovModel();
        //START state
        oMarkovModel.createSTARTState("START");

        RMPInputModelManager oInputModelManager = RMPInputModelManager.getInstance();
        int iNumberOfFailureTypes = oInputModelManager.getNumberOfFailureTypes();

        //F0(SUCCESS), F1, F2 ...., Fm
        for (int j = 0; j < iNumberOfFailureTypes; j++) {
            oMarkovModel.addAbsorbingMarkovState("F" + j);
        }

        //build a Markov model for each retry part.
        for (int i = 0; i < iNumberOfInnerParts; i++) {
            String strSTARTi = "START" + i;
            String strMTCPi = "MTCP" + i;

            oMarkovModel.addTransitiveMarkovState(strSTARTi);
            oMarkovModel.addTransitiveMarkovState(strMTCPi);

            oMarkovModel.addMarkovTransition(strSTARTi, strMTCPi, 1.0);

            for (int j = 0; j < iNumberOfFailureTypes; j++) {
                String strFij = "F" + i + "," + j;
                oMarkovModel.addTransitiveMarkovState(strFij);
                oMarkovModel.addMarkovTransition(strMTCPi, strFij, oEquivalentIAWrappers[i].getFp(j));
            }
        }

        //transition from START->START0
        oMarkovModel.addMarkovTransition("START", "START0", 1.0);

        //for the Markov model of the last retry
        for (int j = 0; j < iNumberOfFailureTypes; j++) {
            oMarkovModel.addMarkovTransition("F" + (iNumberOfInnerParts - 1) + "," + j, "F" + j, 1.0);
        }

        ErrorDetectionMatrixType[] oErrorDetectionMatrices = multiTryCatchStructure.getErrorDetectionMatrixArray();

        RMPErrorDetectionMatrixWrapper[] oEDMatrixWrappers = new RMPErrorDetectionMatrixWrapper[iNumberOfInnerParts - 1];
        for (int i = 0; i < iNumberOfInnerParts - 1; i++) {
            oEDMatrixWrappers[i] = new RMPErrorDetectionMatrixWrapper(oErrorDetectionMatrices[i]);
        }

        //for other Markov models
        for (int i = 0; i < iNumberOfInnerParts - 1; i++) {
            for (int j = 0; j < iNumberOfFailureTypes; j++) {
                double dRemainDetectionProbability = 1.0;
                for (int x = i + 1; x < iNumberOfInnerParts; x++) {
                    //FHx set
                    Collection<String> oFHxSet = getHandledFailureTypesAsStringList(oMultiTryCatchParts[x].getHandledFailureTypeArray());

                    Collection<String> oFHyUnion = new ArrayList<String>();
                    for (int y = i + 1; y < x; y++) {
                        oFHyUnion = CollectionUtils.union(oFHyUnion, getHandledFailureTypesAsStringList(oMultiTryCatchParts[y].getHandledFailureTypeArray()));
                    }

                    //FHix set
                    Collection<String> oFHixSet = CollectionUtils.subtract(oFHxSet, oFHyUnion);
                    Iterator<String> oIterator = oFHixSet.iterator();

                    double dTotalDetectionProbability = 0.0;
                    while (oIterator.hasNext()) {
                        String strFailureTypeName = oIterator.next();
                        int k = oInputModelManager.getFailureTypeIndex(strFailureTypeName);
                        dTotalDetectionProbability += oEDMatrixWrappers[i].getDp(j, k);
                    }

                    oMarkovModel.addMarkovTransition("F" + i + "," + j, "START" + x, dTotalDetectionProbability);
                    dRemainDetectionProbability -= dTotalDetectionProbability;
                }

                oMarkovModel.addMarkovTransition("F" + i + "," + j, "F" + j, dRemainDetectionProbability);
            }
        }


        RMPInternalActivityWrapper oIAWrapper = new RMPInternalActivityWrapper();
        for (int j = 0; j < iNumberOfFailureTypes; j++) {
            oIAWrapper.setFp(j, oMarkovModel.getProbability("F" + j));
        }

        return oIAWrapper;
    }

    /**
     * Convert an array of "handled failure types" - schema type, to a list of "possibly handled failure types", string type.
     * @param handledFailureTypes an array of "handled failure types" - schema type.
     * @return a list of "possibly handled failure types", string type.
     */
    private List getHandledFailureTypesAsStringList(HandledFailureTypeType[] handledFailureTypes) {
        List<String> oReturnList = new ArrayList<String>();

        for (HandledFailureTypeType handledFailureType : handledFailureTypes) {
            oReturnList.add(handledFailureType.getForFailureType());
        }
        return oReturnList;
    }

    /**
     * Get the full provided service name.
     * @param componentInstanceName component instance name.
     * @param providedServiceName a provided service name.
     * @return the full provided service name.
     */
    private String getFullProvidedServiceName(String componentInstanceName, String providedServiceName) {
        return componentInstanceName + RMP_DOT + providedServiceName;
    }
}
